package org.light.domain;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import org.light.core.Writeable;
import org.light.exception.ValidateException;
import org.light.utils.PluralUtil;
import org.light.utils.StringUtil;
import org.light.utils.WriteableUtil;

import net.sf.json.JSONObject;

public class Domain extends Class implements Comparable<Domain>,Serializable{
	private static final long serialVersionUID = -9098947199465839222L;
	protected Set<Method> setters = new TreeSet<Method>();
	protected Set<Method> getters = new TreeSet<Method>();
	protected String domainStr;
	protected Field domainId;
	protected Field domainName;
	protected Field active;
	protected String plural; 
	protected String dbPrefix = "";
	protected String label;
	protected Map<String,String> fieldLabels = new TreeMap<String,String>();
	protected Set<ManyToMany> manyToManies = new TreeSet<ManyToMany>();
	protected String alias = "";
	protected String aliasLabel = "";
	protected String aliasPlural = "";

	public void setAliasPlural(String aliasPlural) {
		this.aliasPlural = aliasPlural;
	}
	
	public Domain decorateCompareTo() throws ValidateException{
		if (StringUtil.isBlank(getDomainId())) {
			ValidateInfo info = new ValidateInfo();
			info.addCompileError("域对象没有设置序号！");
			throw new ValidateException(info);
		}
		Interface inf = new Interface();
		inf.setStandardName("Comparable<"+getStandardName()+">");
		Method method = new Method();
		method.setStandardName("compareTo");
		method.setReturnType(new Type("int"));
		Signature si = new Signature(1, StringUtil.lowerFirst(this.getStandardName()), this.getType());
		method.addSignature(si);
		
		List<Writeable> list = new ArrayList<Writeable>();
		list.add(new Statement(1000L,2,"return this.get"+domainId.getCapFirstFieldName()+"().compareTo("+getLowerFirstDomainName()+".get"+domainId.getCapFirstFieldName()+"());"));
		method.setMethodStatementList(WriteableUtil.merge(list));
		
		inf.addMethod(method);
		this.addClassImplement(inf);
		
		return this;
	}
	
	public long maxSerial(){
		long maxserial = 0;
		for (Field f:this.fields){
			if (f.getSerial() > maxserial) maxserial = f.getSerial();
		}
		return maxserial;
	}

	public Field getDomainId() {
		return domainId;
	}
	public void setDomainId(Field domainId) {
		this.domainId = domainId;
		this.domainId.setSerial(this.maxSerial() + 300);
	}
	
	public void addField(String name,String type){
		if ("active".equalsIgnoreCase(name)||"deleted".equalsIgnoreCase(name)||"delete".equalsIgnoreCase(name)) {
			this.active = new Field(name, "boolean");
			this.active.setSerial(this.maxSerial() + 200);
			return;
		}
		if (name.equalsIgnoreCase(this.getStandardName()+"Name")) {
			this.domainName = new Field(name, "String");
			this.domainName.setSerial(this.maxSerial() + 100);
			return;
		}
		if (name.equalsIgnoreCase(this.getStandardName()+"Id") ||name.equalsIgnoreCase("id")){
			this.domainId = new Field(name, type);
			this.domainId.setSerial(this.maxSerial() + 300);
			return;
		}
		this.fields.add(new Field(this.maxSerial()+100L,name,type));
	}
	
	public boolean existsSetterByName(String methodName){
		for (Method mySetm:this.setters){
			if (mySetm.getStandardName().equalsIgnoreCase(methodName)) return true;
		}
		return false;
	}
	
	public boolean existsGetterByName(String methodName){
		for (Method mySetm:this.getters){
			if (mySetm.getStandardName().equalsIgnoreCase(methodName)) return true;
		}
		return false;
	}
	
	public void generateSetterGetter(Field f){
		String name = f.getFieldName();
		Type type = f.getClassType();
		Method setter = new Method();
		setter.setStandardName("set"+StringUtil.capFirst(name));
		setter.setReturnType(new Type("void"));
		setter.addSignature(new Signature(1,name,type));
		setter.setContent("\t\tthis."+StringUtil.lowerFirst(name)+" = "+StringUtil.lowerFirst(name)+";\n");
		setter.setSerial(f.getSerial());
		if(!existsSetterByName(setter.getStandardName())) this.setters.add(setter);
		
		Method getter = new Method();
		getter.setStandardName("get"+StringUtil.capFirst(name));
		getter.setReturnType(type);
		getter.setContent("\t\treturn this."+StringUtil.lowerFirst(name)+";\n");
		getter.setSerial(f.getSerial()+50);
		if(!existsGetterByName(getter.getStandardName())) this.getters.add(getter);
	}
	
	public void addField(String name,String type,String packageToken){
		this.addField(new Field(name,type,packageToken));
	}
	
	@Override
	public void addField(Field f){
		if (f instanceof Dropdown){
			f.setSerial(this.maxSerial()+100);
			this.fields.add(f);
			return;
		}
		if ("active".equalsIgnoreCase(f.getFieldName())||"deleted".equalsIgnoreCase(f.getFieldName())||"delete".equalsIgnoreCase(f.getFieldName())) {
			this.active = new Field(f.getFieldName(), "Boolean");
			this.active.setSerial(this.maxSerial() + 200);
			return;
		}
		if (f.getFieldName().equals(StringUtil.lowerFirst(this.getStandardName())+"Name")) {
			this.domainName = new Field(f.getFieldName(), "String");
			this.domainName.setSerial(this.maxSerial() + 100);
			return;
		}
		if (f.getFieldName().equals(StringUtil.lowerFirst(this.getStandardName())+"Id")) {
			this.domainId = new Field(f.getFieldName(), f.getRawType());
			this.domainId.setSerial(this.maxSerial() + 300);
			return;
		}
		this.fields.add(new Field(this.maxSerial()+100,f.getFieldName(),f.getFieldType()));
	}
	
	@Override
	public String generateClassString(){
		for (Field f:this.getFields()){
			generateSetterGetter(f);
		}
		StringBuilder sb = new StringBuilder();
		if (this.packageToken !=null && !"".equalsIgnoreCase(this.packageToken)) sb.append("package ").append(this.packageToken).append(".domain;\n\n");
		this.classImports.addAll(this.generateImportStrings());
		for (String s: this.classImports){
			sb.append("import ").append(s).append(";\n");
		}
		sb.append("\n");
		
		for (String s: this.classAnnotations){
			sb.append("@").append(s).append("\n");
		}
		sb.append("public class ");
		sb.append(capFirst(this.standardName)).append(" ");
		List<Interface> implList = new ArrayList<Interface>();
		implList.addAll(classImplements);
		for (int i=0;i<implList.size();i++){
			if (i==0) sb.append("implements ");
			sb.append(implList.get(i).getStandardName());
			if (i<implList.size()-1) sb.append(",");
		}

		sb.append(" {\n");
		Iterator it =  this.getFields().iterator();

        while (it.hasNext()) {
        	Field f = (Field)it.next();
	        String name = f.getFieldName();
	        String type = f.getClassType().getTypeName();
	        sb.append("\tprotected ").append(type).append(" ").append(lowerFirst(name) + ";\n");
        }
        sb.append("\n");
        
        for(Method m: this.getters){
        	sb.append(m.generateMethodString());
        	sb.append("\n");
        }
        
        for(Method m: this.setters){
        	sb.append(m.generateMethodString());
        	sb.append("\n");
        } 
        

        it = this.methods.iterator();
        while (it.hasNext()){
        	sb.append(((Method)it.next()).generateMethodString());
        	sb.append("\n");
        }
        
        for (Interface inf : this.classImplements){
        	for (Method m:inf.getMethods()){
	        	sb.append(m.generateMethodString());
	        	sb.append("\n");
        	}
        }
        sb.append("}\n");
		return sb.toString();
	}
	
	public String capFirst(String value){
		return value.substring(0, 1).toUpperCase()+value.substring(1);
	}
	
	public String lowerFirst(String value){
		return value.substring(0, 1).toLowerCase()+value.substring(1);
	}
	
	public int compareTo(Domain domain) {
		String myName = this.getStandardName();
		String otherName = domain.getStandardName();
		return myName.compareTo(otherName);
	}
	
	public Set<Method> getSetters() {
		return setters;
	}
	public void setSetters(Set<Method> setters) {
		this.setters = setters;
	}
	public Set<Method> getGetters() {
		return getters;
	}
	public void setGetters(Set<Method> getters) {
		this.getters = getters;
	}
	public String getDomainStr() {
		return domainStr;
	}
	public void setDomainStr(String domainStr) {
		this.domainStr = domainStr;
	}
	public Field getDomainName() {
		return domainName;
	}
	public void setDomainName(Field domainName) {
		this.domainName = domainName;
	}
	public Field getActive() {
		return active;
	}
	public void setActive(Field active) {
		this.active = active;
	}
	public String getPlural() {
		if (this.plural == null || "".equals(this.plural)){
			return PluralUtil.lookupPlural(this.standardName);
		} else {
			return StringUtil.capFirst(plural);
		}
	}
	
	public String getAliasPlural() {
		if (StringUtil.isBlank(this.alias)){
			return getPlural();
		}
		if (StringUtil.isBlank(this.aliasPlural)){
			return PluralUtil.lookupPlural(this.getAlias());
		} else {
			return StringUtil.capFirst(this.aliasPlural);
		}
	}
	
	public void setPlural(String plural) {
		this.plural = StringUtil.capFirst(plural);
	}
	
	public Type getType(){
		Type retType = new Type(this.getStandardName(),this.getPackageToken());
		return retType;
	}
	
	@Override
	public Set<Field> getFields(){
		
		Set<Field> set = new TreeSet<Field>(new FieldSerialComparator());
		set.addAll(super.getFields());		
		if (this.domainName != null) {
			this.domainName.setSerial(this.maxSerial() + 100);
			set.add(this.domainName);
		}
		if (this.active != null) {
			this.active.setSerial(this.maxSerial() + 200);
			set.add(this.active);
		}
		if (this.domainId != null) {
			this.domainId.setSerial(this.maxSerial() + 300);
			set.add(this.domainId);
		}
		return set;
	}
	
	public Set<Field> getFieldsWithoutId(){
		Set<Field> set = new TreeSet<Field>();
		set.addAll(super.getFields());		
		if (this.domainName != null) {
			this.domainName.setSerial(this.maxSerial() + 100);
			set.add(this.domainName);
		}
		if (this.active != null) {
			this.active.setSerial(this.maxSerial() + 200);
			set.add(this.active);
		}
		return set;
	}
	
	public Set<Field> getFieldsWithoutIdAndActive(){
		Set<Field> set = new TreeSet<Field>();
		set.addAll(super.getFields());		
		if (this.domainName != null) {
			this.domainName.setSerial(this.maxSerial() + 100);
			set.add(this.domainName);
		}
		return set;
	}
	
	public String generateTypeVarString(){
		return StringUtil.capFirst(this.getStandardName()) + " " + StringUtil.lowerFirst(this.getStandardName());
	}
	
	public Field getField(String fieldName){
		for (Field f : this.getFields()){
			if (f instanceof Dropdown){
				Dropdown dp = (Dropdown) f;
				if (!StringUtil.isBlank(dp.getAliasName())&&dp.getAliasName().equals(fieldName)){
					return dp;
				}
			}else {
				if (!StringUtil.isBlank(f.getFieldName())&&f.getFieldName().equals(fieldName)){
					return f;
				}
			}
		}
		return null;
	}

	public String getDbPrefix() {
		if (dbPrefix == null && "".equals(dbPrefix)) return "";
		else return dbPrefix;
	}

	public void setDbPrefix(String dbPrefix) {
		this.dbPrefix = dbPrefix;
	}
	
	public String getLowerFirstDomainName(){
		return lowerFirst(this.getStandardName());
	}
	
	public String getLowerFirstChar(){
		return lowerFirst(this.getStandardName()).substring(0,1);
	}
	
	public String getCapFirstDomainName(){
		return capFirst(this.getStandardName());
	}
	
	public String getFullName(){
		return this.getPackageToken()+".domain."+this.getCapFirstDomainName();
	}
	public String getDomainActiveStr() throws ValidateException{
		if (this.getActive() == null ){
			ValidateException err = new ValidateException("域对象活跃字段设置错误");
			throw err;
		} else {
			if (this.getActive().getFieldName().toLowerCase().equals("deleted") || this.getActive().getFieldName().toLowerCase().equals("delete")) return "false";
			else return "true";
		}
	}
	
	public Integer getDomainActiveInteger() throws ValidateException{
		if (this.getActive() == null ){
			ValidateException err = new ValidateException("域对象活跃字段设置错误");
			throw err;
		} else {
			if (this.getActive().getFieldName().toLowerCase().equals("deleted") || this.getActive().getFieldName().toLowerCase().equals("delete")) return 0;
			else return 1;
		}
	}
	
	public String getDomainDeletedStr() throws ValidateException{
		if (this.getActive() == null){
			ValidateException err = new ValidateException("域对象活跃字段设置错误");
			throw err;
		} else {
			if (this.getActive().getFieldName().toLowerCase().equals("deleted") || this.getActive().getFieldName().toLowerCase().equals("delete")) return "true";
			else return "false";
		}
	}
	
	public Integer getDomainDeletedInteger() throws ValidateException{
		if (this.getActive() == null){
			ValidateException err = new ValidateException("域对象活跃字段设置错误");
			throw err;
		} else {
			if (this.getActive().getFieldName().toLowerCase().equals("deleted") || this.getActive().getFieldName().toLowerCase().equals("delete")) return 1;
			else return 0;
		}
	}
	
	public String getCapFirstPlural(){
		return StringUtil.capFirst(this.getPlural());
	}
	
	public String getLowerFirstPlural(){
		return StringUtil.lowerFirst(this.getPlural());
	}

	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}
	
	public String getText(){
		if (!StringUtil.isBlank(this.getAliasLabel())){
			return this.getAliasLabel();
		}
		else if (!StringUtil.isBlank(this.getLabel())) {
			return this.label;
		}
		else if (!StringUtil.isBlank(this.getAlias())) {
			return this.getAlias();
		}		
		else return this.getStandardName();
	}
	
	public String getAliasText(){
		if (!StringUtil.isBlank(this.aliasLabel)) return this.aliasLabel;
		else return this.getAlias();
	}

	public Map<String, String> getFieldLabels() {
		return fieldLabels;
	}

	public void setFieldLabels(Map<String, String> fieldLabels) {
		this.fieldLabels = fieldLabels;
	}
	
	public void putFieldLabel(String key,String value){
		this.fieldLabels.put(key, value);
	}
	
	public void decorateDomainWithLabels(){
		for (Field f: this.getFields()){
			String label = this.fieldLabels.get(f.getLowerFirstFieldName());
			if (label!=null && !label.equals("")) f.setLabel(label);
		}
	}

	public boolean equals(Object o){
		return this.standardName.equals(((Domain)o).getStandardName());
	}	
	
	public Object deepClone(){
		try {
			// 将对象写到流里
			OutputStream bo = new ByteArrayOutputStream();
			//OutputStream op = new ObjectOutputStream();
			ObjectOutputStream oo = new ObjectOutputStream(bo);
			oo.writeObject(this);
			
			// 从流里读出来
			InputStream bi = new ByteArrayInputStream(((ByteArrayOutputStream) bo).toByteArray());
			ObjectInputStream oi = new ObjectInputStream(bi);
			return (oi.readObject());
		} catch(Exception e){
			e.printStackTrace();
			return null;
		}
	}

	public String toString(){
		return JSONObject.fromObject(this).toString();
	}
	
	public void setFieldValue(String fieldName, String fieldValue){
		System.out.println("JerryDebug:setFieldValue:"+fieldName+":"+fieldValue+":"+this.getStandardName());
		Field f = this.getField(fieldName);
		f.setFieldValue(fieldValue);
	}

	public ValidateInfo validate(String dbType){
		ValidateInfo info = new ValidateInfo();
		info.setSuccess(true);
		if (dbType.equalsIgnoreCase("mysql")&&!this.getDomainId().getFieldType().equalsIgnoreCase("long")&&!this.getDomainId().getFieldType().equals("int")&&!this.getDomainId().getFieldType().equals("Integer")){
			info.addCompileError("域对象"+this.getStandardName()+ "类型设置错误！");
			info.setSuccess(false);
		}
		for (Field f: this.getFields()){
			String fieldname = f.getFieldName();
			if (!StringUtil.isLowerCaseLetter(fieldname)) {
				info.addCompileError("域对象"+this.getStandardName()+"字段" + fieldname + "未使用小写英文字母开头！");
			}
			if (fieldname.length() >= 2 && !StringUtil.isLowerCaseLetterPosition(fieldname,1)){
				info.addCompileError("域对象"+this.getStandardName()+"字段" + fieldname + "第二个字母未使用小写英文字母！");
			}
//			if (f instanceof Dropdown){
//				Dropdown dp = (Dropdown)f;
//				if (!dp.aliasName.equals(dp.getTarget().getLowerFirstDomainName()+"Id")){
//					info.addCompileError("域对象"+this.getStandardName()+"下列列表"+dp.getAliasName()+"没有以域对象名Id命名");
//				}
//					
//			}
		}
		
		return info;
	}
	
	public void addManyToMany(ManyToMany manyToMany) {
		this.manyToManies.add(manyToMany);
	}

	public String getAlias() {
		return alias;
	}

	public void setAlias(String alias) {
		this.alias = alias;
	}

	public String getAliasLabel() {
		return aliasLabel;
	}

	public void setAliasLabel(String aliasLabel) {
		this.aliasLabel = aliasLabel;
	}

	public Set<ManyToMany> getManyToManies() {
		return manyToManies;
	}

	public void setManyToManies(Set<ManyToMany> manyToManies) {
		this.manyToManies = manyToManies;
	}
	
	public String generateFieldLabelsArrayStr(Collection<Field> fields) {
		StringBuilder sb = new StringBuilder("{");
		for (Field f:fields) {
			sb.append("\"").append(f.getText()).append("\",");
		}
		if (fields!=null && fields.size()>0) {
			sb.deleteCharAt(sb.length()-1);
		}
		sb.append("}");
		return sb.toString();
	}
	
	public String getAliasOrName() {
		if (!StringUtil.isBlank(this.getAlias())) {
			return this.getAlias();
		}else {
			return this.getStandardName();
		}
	}
	
}
